package org.apereo.cas.config; import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.regions.Region; import com.amazonaws.regions.Regions; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; import com.amazonaws.services.dynamodbv2.model.DeleteTableRequest; import com.amazonaws.services.dynamodbv2.model.DescribeTableRequest; import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; import com.amazonaws.services.dynamodbv2.model.KeyType; import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; import com.amazonaws.services.dynamodbv2.model.ScanRequest; import com.amazonaws.services.dynamodbv2.model.ScanResult; import com.amazonaws.services.dynamodbv2.model.TableDescription; import com.amazonaws.services.dynamodbv2.util.TableUtils; import com.google.common.base.Throwables; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.bootstrap.config.PropertySourceLocator; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertiesPropertySource; import org.springframework.core.env.PropertySource; import java.net.InetAddress; import java.util.Map; import java.util.Properties; /** * This is {@link DynamoDbCloudConfigBootstrapConfiguration}. * * @author Misagh Moayyed * @since 5.1.0 */ @Configuration("dynamoDbCloudConfigBootstrapConfiguration") public class DynamoDbCloudConfigBootstrapConfiguration implements PropertySourceLocator { private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbCloudConfigBootstrapConfiguration.class); private static final String TABLE_NAME = "DynamoDbCasProperties"; private static final long PROVISIONED_THROUGHPUT = 10; private enum ColumnNames { ID("id"), NAME("name"), VALUE("value"); private final String name; ColumnNames(final String name) { this.name = name; } public String getName() { return name; } } @Override public PropertySource<?> locate(final Environment environment) { final AmazonDynamoDBClient amazonDynamoDBClient = getAmazonDynamoDbClient(environment); createSettingsTable(amazonDynamoDBClient, false); final ScanRequest scan = new ScanRequest(TABLE_NAME); LOGGER.debug("Scanning table with request [{}]", scan); final ScanResult result = amazonDynamoDBClient.scan(scan); LOGGER.debug("Scanned table with result [{}]", scan); final Properties props = new Properties(); result.getItems() .stream() .map(DynamoDbCloudConfigBootstrapConfiguration::retrieveSetting) .forEach(p -> props.put(p.getKey(), p.getValue())); return new PropertiesPropertySource(getClass().getSimpleName(), props); } private static Pair<String, Object> retrieveSetting(final Map<String, AttributeValue> entry) { final String name = entry.get(ColumnNames.NAME).getS(); final String value = entry.get(ColumnNames.VALUE).getS(); return Pair.of(name, value); } private static String getSetting(final Environment environment, final String key) { return environment.getProperty("cas.spring.cloud.dynamodb." + key); } private static AmazonDynamoDBClient getAmazonDynamoDbClient(final Environment environment) { final ClientConfiguration cfg = new ClientConfiguration(); try { final String localAddress = getSetting(environment, "localAddress"); if (StringUtils.isNotBlank(localAddress)) { cfg.setLocalAddress(InetAddress.getByName(localAddress)); } } catch (final Exception e) { LOGGER.error(e.getMessage(), e); } final String key = getSetting(environment, "credentialAccessKey"); final String secret = getSetting(environment, "credentialSecretKey"); final AWSCredentials credentials = new BasicAWSCredentials(key, secret); final AmazonDynamoDBClient client; if (credentials == null) { client = new AmazonDynamoDBClient(cfg); } else { client = new AmazonDynamoDBClient(credentials, cfg); } final String endpoint = getSetting(environment, "endpoint"); if (StringUtils.isNotBlank(endpoint)) { client.setEndpoint(endpoint); } final String region = getSetting(environment, "region"); if (StringUtils.isNotBlank(region)) { client.setRegion(Region.getRegion(Regions.valueOf(region))); } final String regionOverride = getSetting(environment, "regionOverride"); if (StringUtils.isNotBlank(regionOverride)) { client.setSignerRegionOverride(regionOverride); } return client; } private static void createSettingsTable(final AmazonDynamoDBClient amazonDynamoDBClient, final boolean deleteTables) { try { final CreateTableRequest request = new CreateTableRequest() .withAttributeDefinitions(new AttributeDefinition(ColumnNames.ID.getName(), ScalarAttributeType.S)) .withKeySchema(new KeySchemaElement(ColumnNames.ID.getName(), KeyType.HASH)) .withProvisionedThroughput(new ProvisionedThroughput(PROVISIONED_THROUGHPUT, PROVISIONED_THROUGHPUT)) .withTableName(TABLE_NAME); if (deleteTables) { final DeleteTableRequest delete = new DeleteTableRequest(request.getTableName()); LOGGER.debug("Sending delete request [{}] to remove table if necessary", delete); TableUtils.deleteTableIfExists(amazonDynamoDBClient, delete); } LOGGER.debug("Sending delete request [{}] to create table", request); TableUtils.createTableIfNotExists(amazonDynamoDBClient, request); LOGGER.debug("Waiting until table [{}] becomes active...", request.getTableName()); TableUtils.waitUntilActive(amazonDynamoDBClient, request.getTableName()); final DescribeTableRequest describeTableRequest = new DescribeTableRequest().withTableName(request.getTableName()); LOGGER.debug("Sending request [{}] to obtain table description...", describeTableRequest); final TableDescription tableDescription = amazonDynamoDBClient.describeTable(describeTableRequest).getTable(); LOGGER.debug("Located newly created table with description: [{}]", tableDescription); } catch (final Exception e) { throw Throwables.propagate(e); } } }